// <copyright file="MainWindow.xaml.cs" company="Snowman Consulting">
//     Copyright (c) 2009 Snowman Consulting. All rights reserved.
// </copyright>

namespace SnowmanConsulting.ProductKeyManager.UI
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    using SnowmanConsulting.ProductKeyManager.Data;
    using SnowmanConsulting.ProductKeyManager.Logging;
    using SnowmanConsulting.ProductKeyManager.Search;
    using SnowmanConsulting.ProductKeyManager.Xml;
    using System.Windows.Interop;
    using System.Windows.Media;

    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public sealed partial class MainWindow : System.Windows.Window
    {
        /// <summary>
        /// The current name sort direction
        /// </summary>
        private SortDirection nameSort = SortDirection.None;

        /// <summary>
        /// The current name sort direction
        /// </summary>
        private SortDirection keyCountSort = SortDirection.None;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindow"/> class.
        /// </summary>
        public MainWindow()
        {
            Logger.WriteLine(LogItemType.ApplicationStart);

            InitializeComponent();
            this.Loaded += new RoutedEventHandler(this.WindowOnLoaded);
            this.Closing += new CancelEventHandler(this.Window_Closing);

            try
            {
                MainWindow.Products.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(this.Products_CollectionChanged);
            }
            catch (Exception ex)
            {
                Logger.WriteLine(LogItemType.Exception, ex);
            }
        }

        /// <summary>
        /// Gets the Products.
        /// </summary>
        /// <value>The Products.</value>
        private static ProductCollection Products
        {
            get
            {
                if (Properties.Settings.Default.Products == null)
                {
                    Properties.Settings.Default.Products = new ProductCollection();
                    Properties.Settings.Default.Save();
                }

                return Properties.Settings.Default.Products;
            }
        }

        /// <summary>
        /// Gets the search text.
        /// </summary>
        /// <value>The search text.</value>
        private string SearchText
        {
            get
            {
                return this.SearchBox.Text.ToLower(CultureInfo.CurrentCulture);
            }
        }

        /// <summary>
        /// Parses the name of the sort.
        /// </summary>
        /// <param name="name">The name of hte sort direction.</param>
        /// <returns>The sort direction enum value</returns>
        private static SortDirection ParseSortName(string name)
        {
            try
            {
                return (SortDirection)Enum.Parse(typeof(SortDirection), name, true);
            }
            catch (ArgumentException)
            {
                return SortDirection.None;
            }
        }

        /// <summary>
        /// Prompts the export XSL.
        /// </summary>
        /// <returns>Whether or not to Export XSL</returns>
        private static bool PromptExportXsl()
        {
            bool generateStyleSheet = false;

            if (Properties.Settings.Default.EnableXslExport)
            {
                generateStyleSheet = true;
            }
            else
            {
                generateStyleSheet = false;
            }

            return generateStyleSheet;
        }

        /// <summary>
        /// Merges the products.
        /// </summary>
        /// <param name="products">The products.</param>
        private static void MergeProducts(ProductCollection products)
        {
            if (products == null)
            {
                return;
            }

            foreach (Product p in products)
            {
                IEnumerable<Product> matches = (from ep in MainWindow.Products where ep.Name == p.Name select ep);

                if (matches.Count() == 0)
                {
                    MainWindow.Products.Add(p);
                    if (p.Icon == null)
                    {
                        p.Icon = ProductIconHelper.GenericApplicationIcon;
                    }
                }
                else
                {
                    if (Properties.Settings.Default.AlwaysMergeSameNamedImportedProducts)
                    {
                        MergeKeys(matches.First().Keys, p.Keys);
                    }
                }
            }
        }

        /// <summary>
        /// Merges the keys.
        /// </summary>
        /// <param name="existingProductKeys">The existing product keys.</param>
        /// <param name="importedProductKeys">The imported product keys.</param>
        private static void MergeKeys(ProductKeyCollection existingProductKeys, ProductKeyCollection importedProductKeys)
        {
            foreach (ProductKey newPk in importedProductKeys)
            {
                IEnumerable<ProductKey> match = (from keyMatch in existingProductKeys 
                                    where keyMatch.Code == newPk.Code 
                                    && keyMatch.AllocatedTo == newPk.AllocatedTo 
                                    && keyMatch.Notes == newPk.Notes 
                                    select keyMatch);

                if (match.Count() == 0)
                {
                    existingProductKeys.Add(newPk);
                }
            }
        }

        /// <summary>
        /// Handles the CollectionChanged event of the Products control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        private void Products_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// Windows the on loaded.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void WindowOnLoaded(object sender, RoutedEventArgs e)
        {
            try
            {
                // Obtain the window handle for WPF application
                IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
                HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
                mainWindowSrc.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);

                // Get System Dpi
                System.Drawing.Graphics desktop = System.Drawing.Graphics.FromHwnd(mainWindowPtr);
                float DesktopDpiX = desktop.DpiX;
                float DesktopDpiY = desktop.DpiY;

                // Set Margins
                NonClientRegionAPI.MARGINS margins = new NonClientRegionAPI.MARGINS();

                // Extend glass frame into client area
                // Note that the default desktop Dpi is 96dpi. The  margins are
                // adjusted for the system Dpi.
                margins.cxLeftWidth = Convert.ToInt32(5 * (DesktopDpiX / 96));
                margins.cxRightWidth = Convert.ToInt32(5 * (DesktopDpiX / 96));
                margins.cyTopHeight = Convert.ToInt32(((int)this.MenuDock.ActualHeight + 5) * (DesktopDpiX / 96));
                margins.cyBottomHeight = Convert.ToInt32(5 * (DesktopDpiX / 96));

                int hr = NonClientRegionAPI.DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
                //
                if (hr < 0)
                {
                    //DwmExtendFrameIntoClientArea Failed
                    this.MenuDock.Background = Brushes.Gainsboro;
                    Application.Current.MainWindow.Background = Brushes.Gainsboro;
                }
                else
                {
                }
            }
            // If not Vista, paint background white.
            catch (DllNotFoundException)
            {
                this.MenuDock.Background = Brushes.Gainsboro;
                Application.Current.MainWindow.Background = Brushes.Gainsboro;
            }

            try
            {
                this.SetView();

                this.SearchBox.Search += new RoutedEventHandler(this.SearchBox_Search);

                this.nameSort = Properties.Settings.Default.SortNameDirection;
                this.keyCountSort = Properties.Settings.Default.SortKeyCountDirection;
                this.OnNameSort(null, e); 
                this.OnKeyCountSort(null, e);
            }
            catch (Exception ex)
            {
                Logger.WriteLine(LogItemType.Exception, string.Format(CultureInfo.CurrentCulture, "Exception: {0} \r\n {1}", ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// Sets the view.
        /// </summary>
        private void SetView()
        {
            this.listProductKeys.DataContext = MainWindow.Products;

            this.SortAndSearch();
        }

        /// <summary>
        /// Gets the product view.
        /// </summary>
        /// <returns>The collection view</returns>
        private ICollectionView GetProductView()
        {
            return CollectionViewSource.GetDefaultView(listProductKeys.DataContext);
        }

        /// <summary>
        /// Sorts and searches.
        /// </summary>
        private void SortAndSearch()
        {
            ICollectionView view = this.GetProductView();
            view.SortDescriptions.Clear();

            view.GroupDescriptions.Clear();

            if (string.IsNullOrEmpty(this.SearchText))
            {
                view.Filter = null;
            }
            else
            {
                if (view.Filter == null)
                {
                    view.Filter = delegate(object item)
                    {
                        Product product = item as Product;
                        if (product != null && !string.IsNullOrEmpty(this.SearchText))
                        {
                            return product.Name.ContainsKeywords(this.SearchText);
                        }

                        return false;
                    };
                }
            }

            switch (this.nameSort)
            {
                case SortDirection.Ascending:
                    view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
                    break;
                case SortDirection.Descending:
                    view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
                    break;
                default:
                    break;
            }

            switch (this.keyCountSort)
            {
                case SortDirection.Ascending:
                    view.SortDescriptions.Add(new SortDescription("Keys.Count", ListSortDirection.Ascending));
                    break;
                case SortDirection.Descending:
                    view.SortDescriptions.Add(new SortDescription("Keys.Count", ListSortDirection.Descending));
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Called when sorting by name.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void OnNameSort(object sender, RoutedEventArgs e)
        {
            MenuViewSortNameNone.IsChecked = false;
            MenuViewSortNameAsc.IsChecked = false;
            MenuViewSortNameDsc.IsChecked = false;
            if (sender != null)
            {
                this.nameSort = ParseSortName(((MenuItem)sender).Header.ToString());
            }

            switch (this.nameSort)
            {
                case SortDirection.None:
                    MenuViewSortNameNone.IsChecked = true;
                    break;
                case SortDirection.Ascending:
                    MenuViewSortNameAsc.IsChecked = true;
                    break;
                case SortDirection.Descending:
                    MenuViewSortNameDsc.IsChecked = true;
                    break;
            }

            this.SortAndSearch();

            Properties.Settings.Default.SortNameDirection = this.nameSort;
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// Called when sorting by name.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void OnKeyCountSort(object sender, RoutedEventArgs e)
        {
            this.MenuViewSortKeyCountNone.IsChecked = false;
            this.MenuViewSortKeyCountAsc.IsChecked = false;
            this.MenuViewSortKeyCountDsc.IsChecked = false;
            if (sender != null)
            {
                this.keyCountSort = ParseSortName(((MenuItem)sender).Header.ToString());
            }

            switch (this.keyCountSort)
            {
                case SortDirection.None:
                    this.MenuViewSortKeyCountNone.IsChecked = true;
                    break;
                case SortDirection.Ascending:
                    this.MenuViewSortKeyCountAsc.IsChecked = true;
                    break;
                case SortDirection.Descending:
                    this.MenuViewSortKeyCountDsc.IsChecked = true;
                    break;
            }

            this.SortAndSearch();

            Properties.Settings.Default.SortKeyCountDirection = this.keyCountSort;
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// Handles the Search event of the SearchBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void SearchBox_Search(object sender, RoutedEventArgs e)
        {
            this.SetView();
        }

        /// <summary>
        /// Called when the New button is clicked
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void NewButtonClicked(object sender, RoutedEventArgs e)
        {
            ProductDialog newDlg = new ProductDialog();
            newDlg.Owner = this;
            newDlg.ShowDialog();
            MainWindow.Products.Add(newDlg.Product);
            this.SetView();
        }

        /// <summary>
        /// Called when the Edit button is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void EditButtonClicked(object sender, RoutedEventArgs e)
        {
            ProductDialog newDlg = new ProductDialog();
            newDlg.Mode = DialogMode.EditProduct;
            newDlg.Owner = this;
            newDlg.Product = ((Product)listProductKeys.SelectedItem);
            newDlg.ShowDialog();

            this.SetView();
        }

        /// <summary>
        /// Called when the Delete button is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteButtonClicked(object sender, RoutedEventArgs e)
        {
            if (this.listProductKeys.SelectedItem != null)
            {
                MessageBoxResult result = MessageBox.Show(
                    this,
                    string.Format(CultureInfo.CurrentCulture, "Are you sure you want to delete '{0}'?", ((Product)listProductKeys.SelectedItem).Name) + " " + "This will remove the Product and all Keys permanently.",
                    "Delete Product Key?",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Warning);

                if (result == MessageBoxResult.Yes)
                {
                    MainWindow.Products.Remove(((Product)listProductKeys.SelectedItem));

                    this.SetView();
                }
            }
        }

        /// <summary>
        /// Called when the Exit menu item is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void FileMenuExitClicked(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Called when the Import button is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ImportButtonClicked(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog opendlg = new Microsoft.Win32.OpenFileDialog();
            opendlg.DefaultExt = "xml";
            opendlg.Filter = "Product Key Manager XML|*.xml;*.pkml|MSDN XML|*.xml|XAML|*.xaml|All formats|*.xml;*.pkml;*.xaml";
            if ((bool)opendlg.ShowDialog())
            {
                Mouse.OverrideCursor = Cursors.Wait;
                IXmlHandler handler;

                string extension = opendlg.FileName.Substring(opendlg.FileName.LastIndexOf('.') + 1);

                switch (extension)
                {
                    case "xaml":
                        handler = new XamlHandler();
                        handler.Load(new Uri(opendlg.FileName));
                        break;
                    case "pkml":
                        handler = new ProductKeyManagerXmlHandler();
                        handler.Load(new Uri(opendlg.FileName));
                        break;
                    case "xml":
                    default:
                        handler = XmlHelper.Load(new Uri(opendlg.FileName));
                        break;
                }
                
                if (handler.Products != null)
                {
                    MainWindow.MergeProducts(handler.Products);
                }
            }

            this.SetView();
            Mouse.OverrideCursor = Cursors.Arrow;
        }

        /// <summary>
        /// Called when the About button is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void MenuHelpAboutClicked(object sender, RoutedEventArgs e)
        {
            UI.AboutDialog helpWin = new UI.AboutDialog();
            helpWin.Owner = this;
            helpWin.ShowDialog();
        }

        /// <summary>
        /// Called on keypress in the product list box. We are looking for the delete key to delete selected keys.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        private void ListBoxKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Delete)
            {
                this.DeleteButtonClicked(sender, (RoutedEventArgs)e);
            }
        }

        /// <summary>
        /// Handles the SelectionChanged event of the listProductKeys control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> instance containing the event data.</param>
        private void ProductKeysList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            try
            {
                bool isSelected = false;
                if (listProductKeys.SelectedIndex > -1)
                {
                    isSelected = true;
                }

                this.MenuEdit.Visibility = isSelected ? Visibility.Visible : Visibility.Collapsed;
                this.EditButton.Visibility = isSelected ? Visibility.Visible : Visibility.Collapsed;
                this.DeleteButton.Visibility = isSelected ? Visibility.Visible : Visibility.Collapsed;
            }
            catch (Exception ex)
            {
                Logger.WriteLine(LogItemType.Exception, string.Format(CultureInfo.CurrentCulture, "Exception: {0} \r\n {1}", ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// Handles the DoubleClicked event of the listProductKeys control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ProductKeysList_DoubleClicked(object sender, RoutedEventArgs e)
        {
            if (this.listProductKeys.SelectedIndex > -1)
            {
                ProductDialog viewWindow = new ProductDialog();
                viewWindow.Mode = DialogMode.ViewProduct;
                viewWindow.Product = ((Product)this.listProductKeys.SelectedItem);
                viewWindow.Owner = this;
                viewWindow.ShowDialog();
            }
        }

        /// <summary>
        /// Files the menu export XML clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ExportButtonClicked(object sender, RoutedEventArgs e)
        {
            bool isXaml = false;
            bool generateStyleSheet = false;
            string stylesheet = string.Empty;
            Microsoft.Win32.SaveFileDialog save = new Microsoft.Win32.SaveFileDialog();
            save.Filter = "Product Key Manager XML|*.xml;*.pkml|MSDN XML|*.xml|XAML|*.xaml";

            if ((bool)save.ShowDialog())
            {
                IXmlHandler handler;

                string extension = save.FileName.Substring(save.FileName.LastIndexOf('.') + 1);

                switch (extension)
                {
                    case "xaml":
                        handler = new XamlHandler();
                        handler.GenerateXml(MainWindow.Products);
                        isXaml = true;
                        break;
                    case "pkml":
                        handler = new ProductKeyManagerXmlHandler();
                        handler.GenerateXml(MainWindow.Products);
                        stylesheet = Properties.Settings.Default.ProductKeyManagerXmlStylesheet;
                        break;
                    case "xml":
                    default:
                        if (save.FilterIndex == 1)
                        {
                            handler = new ProductKeyManagerXmlHandler();
                            handler.GenerateXml(MainWindow.Products);
                            stylesheet = Properties.Settings.Default.ProductKeyManagerXmlStylesheet;
                        }
                        else
                        {
                            handler = new MsdnXmlHandler();
                            handler.GenerateXml(MainWindow.Products);
                            stylesheet = Properties.Settings.Default.MsdnXmlStyleSheet;
                        }

                        break;
                }

                if (!isXaml)
                {
                    generateStyleSheet = PromptExportXsl();
                    stylesheet = stylesheet.Replace("\"\"", "\"");
                }

                Mouse.OverrideCursor = Cursors.Arrow;

                XmlHelper.SaveProductKeyXml(generateStyleSheet, save.FileName, handler.GeneratedXml, stylesheet);

                Mouse.OverrideCursor = Cursors.Arrow;
            }
        }

        /// <summary>
        /// Handles the Closing event of the Window control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void Window_Closing(object sender, CancelEventArgs e)
        {
            try
            {
                Properties.Settings.Default.Save();
            }
            catch (Exception ex)
            {
                Logger.WriteLine(LogItemType.Exception, ex);
            }

            Logger.WriteLine(LogItemType.ApplicationEnd);
        }

        /// <summary>
        /// Handles the Click event of the ToolsSettings control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void ToolsSettings_Click(object sender, RoutedEventArgs e)
        {
            Settings settingsWindow = new Settings();
            settingsWindow.Owner = this;
            settingsWindow.ShowDialog();
            this.OnNameSort(null, e);
            this.OnKeyCountSort(null, e);
            this.SetView();
        }

        /// <summary>
        /// Handles the Click event of the MenuToolsLogViewer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void MenuToolsLogViewer_Click(object sender, RoutedEventArgs e)
        {
            LogViewer logViewer = new LogViewer();
            logViewer.ShowDialog();
        }
    }
}